home *** CD-ROM | disk | FTP | other *** search
Wrap
# Source Generated with Decompyle++ # File: in.pyc (Python 2.4) from __future__ import division import os import re import sys import time import socket import cPickle import textwrap import threading import webbrowser from win32com.shell import shell, shellcon import wx import wx.lib.newevent as wx from wxp import * from spamexperts import license from spamexperts import Version from spamexperts import dnslookup from spamexperts import software_update from spamexperts.message import SEHeaderMessage from spamexperts.Options import options, optionsPathname from spamexperts.OptionsClass import IS_HAM, IS_SPAM, IS_UNSURE from model import VIEW_SPAM, VIEW_HAM, VIEW_BLACK, VIEW_WHITE, VIEW_UNSURE from spamexperts.resources import get_image_filename, application_directory (ExitModel, EVT_EXIT_MODEL) = wx.lib.newevent.NewEvent() (DoUpdate, EVT_DO_UPDATE) = wx.lib.newevent.NewEvent() (ConcludeUpdate, EVT_CONCLUDE_UPDATE) = wx.lib.newevent.NewEvent() (SubmitReport, EVT_SUBMIT_REPORT) = wx.lib.newevent.NewEvent() (WelcomeEvent, EVT_DO_WELCOME) = wx.lib.newevent.NewEvent() (ProButtonEvent, EVT_PRO_BUTTON) = wx.lib.newevent.NewEvent() TYPE_EMAIL_MSGS = 0 TYPE_EMAIL_ADDRESS = 1 class ButtonDropTarget(wx.PyDropTarget): def __init__(self, handler, model, acceptedData): wx.PyDropTarget.__init__(self) self.handler = handler self.model = model self.acceptedData = acceptedData self.data = wx.CustomDataObject('SEDrag') self.SetDataObject(self.data) def OnEnter(self, x, y, d): return d def OnLeave(self): pass def OnDrop(self, x, y): return True def OnDragOver(self, x, y, d): if hasattr(self.model, 'drag') and self.model.drag in self.acceptedData: return wx.DragMove return wx.DragNone def OnData(self, x, y, d): if self.GetData(): cargo = self.data.GetData() list = cPickle.loads(cargo) self.handler(list) return d class ListDropTarget(wx.PyDropTarget): def __init__(self, handler): wx.PyDropTarget.__init__(self) self.handler = handler self.data = wx.CustomDataObject('SEListDrag') self.SetDataObject(self.data) self.match = False def OnEnter(self, x, y, d): return d def OnLeave(self): pass def OnDrop(self, x, y): return True def OnDragOver(self, x, y, d): if self.match: return wx.DragMove return wx.DragNone def OnData(self, x, y, d): if self.GetData(): cargo_pickle = self.data.GetData() cargo = cPickle.loads(cargo_pickle) self.handler(cargo) return d class ButtonBar(BoxPanel): def __init__(self, parent, model): BoxPanel.__init__(self, parent) self.SetBackgroundColour((225, 225, 225)) self.spam = ImgButton(self, '', wx.Image(get_image_filename('spam-normal'))) self.unsure = ImgButton(self, '', wx.Image(get_image_filename('unsure-normal'))) self.ham = ImgButton(self, '', wx.Image(get_image_filename('ham-normal'))) self.black = ImgButton(self, '', wx.Image(get_image_filename('blacklist-normal'))) self.white = ImgButton(self, '', wx.Image(get_image_filename('whitelist-normal'))) self.UpdateText() self.buttons = [ self.spam, self.unsure, self.ham, self.black, self.white] self.sizer.AddSpacer((-1, 21)) self.Add(self.ham, 0, wx.EXPAND | wx.ALL, 5) self.Add(self.unsure, 0, wx.EXPAND | wx.ALL, 5) self.Add(self.spam, 0, wx.EXPAND | wx.ALL, 5) self.Add(self.black, 0, wx.EXPAND | wx.ALL, 5) self.Add(self.white, 0, wx.EXPAND | wx.ALL, 5) self.spam.Bind(wx.EVT_BUTTON, self.OnSpam) self.unsure.Bind(wx.EVT_BUTTON, self.OnUnsure) self.ham.Bind(wx.EVT_BUTTON, self.OnHam) self.white.Bind(wx.EVT_BUTTON, self.OnWhite) self.black.Bind(wx.EVT_BUTTON, self.OnBlack) self.spam.SetDropTarget(ButtonDropTarget(self.OnDropSpam, model, [ 'ham', 'unsure'])) self.unsure.SetDropTarget(ButtonDropTarget(self.OnDropUnsure, model, [ 'ham', 'spam'])) self.ham.SetDropTarget(ButtonDropTarget(self.OnDropHam, model, [ 'spam', 'unsure'])) self.white.SetDropTarget(ButtonDropTarget(self.OnDropWhite, model, [ 'spam', 'ham', 'unsure', 'black'])) self.black.SetDropTarget(ButtonDropTarget(self.OnDropBlack, model, [ 'spam', 'ham', 'unsure', 'white'])) model.AddView(self) self.model = model def UpdateText(self): self.spam.SetTitle(_('Spam')) self.unsure.SetTitle(_('Unsure')) self.ham.SetTitle(_('Not Spam')) self.black.SetTitle(_('Blocked Senders')) self.white.SetTitle(_('Allowed Senders')) self.spam.SetHelpText(_("Emails on this page have been classified as 'spam'. If a mistake has been made please drag & drop the message to the 'Not Spam' button.")) self.ham.SetHelpText(_("Emails on this page have been classified as 'not spam'. If a mistake has been made please drag & drop the message to the 'Spam' button.")) self.unsure.SetHelpText(_("SpamExperts was unable to correctly classify the emails found on this page. Please drag & drop the emails to the appropriate 'Spam'/'Not Spam' button.")) self.white.SetHelpText(_("Emails sent from addresses on this page will always be considered 'not spam' and will bypass all filtering technologies. The content of the message will not be trained and therefore it will not improve the smartness of SpamExperts.")) self.black.SetHelpText(_("Emails sent from addresses on this page will always be considered 'spam' and will bypass all filtering technologies. The content of the message will not be trained and therefore it will not improve the smartness of SpamExperts.")) self.Layout() def UpdateView(self, model): map((lambda cntr: cntr.SetToggle(False)), self.buttons) self.buttons[model.active].SetToggle(True) def OnDropSpam(self, drop_contents): pass def OnDropHam(self, drop_contents): pass def OnDropUnsure(self, drop_contents): pass def OnDropWhite(self, drop_contents): pass def OnDropBlack(self, drop_contents): pass def OnHam(self, evt): self.model.UpdateViews(True) self.model.SetActive(VIEW_HAM) def OnSpam(self, evt): self.model.UpdateViews(True) self.model.SetActive(VIEW_SPAM) def OnUnsure(self, evt): self.model.UpdateViews(True) self.model.SetActive(VIEW_UNSURE) def OnWhite(self, evt): self.model.SetActive(VIEW_WHITE) def OnBlack(self, evt): self.model.SetActive(VIEW_BLACK) class EmailView(BoxPanel): def __init__(self, parent, model): BoxPanel.__init__(self, parent) self.model = model self.subject = wx.StaticText(self, style = wx.ST_NO_AUTORESIZE) self.sender = wx.StaticText(self, style = wx.ST_NO_AUTORESIZE) self.recipient = wx.StaticText(self, style = wx.ST_NO_AUTORESIZE) self.subject_label = wx.StaticText(self) self.sender_label = wx.StaticText(self) self.recipient_label = wx.StaticText(self) self.UpdateText() try: font = wx.Font(-1, wx.DEFAULT, wx.NORMAL, wx.BOLD) except wx.PyAssertionError: font = wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.BOLD) self.subject_label.SetFont(font) self.sender_label.SetFont(font) self.recipient_label.SetFont(font) self.body = wx.TextCtrl(self, style = wx.TE_MULTILINE | wx.TE_RICH2 | wx.TE_READONLY) self.subj_sizer = wx.BoxSizer(wx.HORIZONTAL) self.subj_sizer.Add(self.subject_label, 0) self.subj_sizer.Add(self.subject, 1, wx.EXPAND) self.Add(self.subj_sizer, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND, 3) self.send_sizer = wx.BoxSizer(wx.HORIZONTAL) self.send_sizer.Add(self.sender_label, 0) self.send_sizer.Add(self.sender, 1, wx.EXPAND) self.Add(self.send_sizer, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND, 3) self.recp_sizer = wx.BoxSizer(wx.HORIZONTAL) self.recp_sizer.Add(self.recipient_label, 0) self.recp_sizer.Add(self.recipient, 1, wx.EXPAND) self.Add(self.recp_sizer, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND, 3) self.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 3) self.Add(self.body, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 3) def UpdateText(self): self.subject_label.SetTitle(_('Subject:')) self.subject_label.SetBestFittingSize() self.sender_label.SetTitle(_('From:')) self.sender_label.SetBestFittingSize() self.recipient_label.SetTitle(_('To:')) self.recipient_label.SetBestFittingSize() whitespace_re = re.compile('\\s+') def SetHeader(self, element, header): header = self.whitespace_re.sub(' ', header) element.SetLabel(' ' + header) best_width = element.GetBestSize()[0] actual_width = element.GetSize()[0] if best_width > actual_width: new_length = int((actual_width / best_width) * len(header)) header = header[:new_length - 10] + '...' element.SetLabel(' ' + header) def SetMsg(self, msg): self.SetHeader(self.subject, msg.subject) self.SetHeader(self.sender, msg.sender) self.SetHeader(self.recipient, msg.recipient) self.body.SetValue(msg.body) class ClassifierList(SortedLC): def __init__(self, parent, icon): SortedLC.__init__(self, parent) self.itemDataMap = None self.selecting = False self.il.Add(icon) self.UpdateText() self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown) self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) self.Bind(wx.EVT_CHAR, self.OnChar) def UpdateText(self): self.columns = [ (_('From'), 150), (_('Subject'), 220), (_('Date'), 100)] self.ResetColumns() def DeselectAll(self): self.selecting = True for i in xrange(self.GetItemCount()): self.SetItemState(i, 0, wx.LIST_STATE_SELECTED) self.selecting = False def SelectAll(self): self.selecting = True for i in xrange(self.GetItemCount()): self.SetItemState(i, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) self.selecting = False def OnChar(self, evt): if evt.ControlDown() and evt.GetKeyCode() == 1: self.SelectAll() elif evt.GetKeyCode() == wx.WXK_DELETE: self.GetParent().view.RemoveFromList(None) evt.Skip() def UpdateData(self, data): self.Show(0) self.columns = [ (_('From'), self.GetColumnWidth(0)), (_('Subject'), self.GetColumnWidth(1)), (_('Date'), self.GetColumnWidth(2))] self.itemDataMap = data sortTuple = (self._col, self._colSortFlag[self._col]) focus = self.GetFocusedItem() select = self.GetFirstSelected() selected = [] if select != -1: while True: select = self.GetNextSelected(select) if select == -1: break selected.append(select) self.ClearAll() self.ResetColumns() for key, data in self.itemDataMap.items(): index = self.InsertImageStringItem(sys.maxint, data[0], 2) self.SetStringItem(index, 1, data[1]) self.SetStringItem(index, 2, time.asctime(time.localtime(data[2]))) self.SetItemData(index, key) if data[4]: self.SetItemTextColour(index, data[4]) continue self.SortListItems(*sortTuple) for select in selected: self.Select(select) self.Focus(focus) self.Show(1) def ResetColumns(self): for column, width in self.columns: idx = self.columns.index((column, width)) self.InsertColumn(idx, column) self.SetColumnWidth(idx, width) def OnRightDown(self, evt): self.pos = evt.GetPosition() (item, flags) = self.HitTest(self.pos) if flags & wx.LIST_HITTEST_ONITEM: if not self.GetItemState(item, wx.LIST_STATE_SELECTED): self.DeselectAll() self.Select(item) evt.Skip() def OnRightUp(self, evt): if not hasattr(self, 'pos'): self.pos = evt.GetPosition() (item, flags) = self.HitTest(self.pos) if flags & wx.LIST_HITTEST_ONITEM: menu = List2Menu(self, self.menuData) self.PopupMenu(menu, self.pos) evt.Skip() def GetSelectedMsgs(self): ret = [] index = self.GetFirstSelected() while index != -1: ret.append(self.itemDataMap[self.GetItemData(index)]) index = self.GetNextSelected(index) return ret class MailList(AutoWidthSortedLC): def __init__(self, parent, icon): AutoWidthSortedLC.__init__(self, parent) self.itemDataMap = None self.il.Add(icon) self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown) self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) self.Bind(wx.EVT_CHAR, self.OnChar) def DeselectAll(self): for i in xrange(self.GetItemCount()): self.SetItemState(i, 0, wx.LIST_STATE_SELECTED) def SelectAll(self): for i in xrange(self.GetItemCount()): self.SetItemState(i, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) def OnChar(self, evt): if evt.ControlDown() and evt.GetKeyCode() == 1: self.SelectAll() elif evt.GetKeyCode() == wx.WXK_DELETE: self.GetParent().Delete(None) elif evt.GetKeyCode() == wx.WXK_DOWN: self.MoveSelectionDown(deselect = not evt.ShiftDown()) elif evt.GetKeyCode() == wx.WXK_UP: self.MoveSelectionUp(deselect = not evt.ShiftDown()) elif evt.GetKeyCode() == wx.WXK_PAGEUP: self.ScrollPages(-1) elif evt.GetKeyCode() == wx.WXK_PAGEDOWN: self.ScrollPages(1) def UpdateData(self, data): if data != self.itemDataMap: self.itemDataMap = data focus = self.GetFocusedItem() select = self.GetFirstSelected() selected = [] if select != -1: while True: select = self.GetNextSelected(select) if select == -1: break selected.append(select) self.ClearAll() self.InsertColumn(0, _('Email')) for key, data in self.itemDataMap.items(): index = self.InsertImageStringItem(sys.maxint, data, 2) self.SetItemData(index, key) for select in selected: self.Select(select) self.Focus(focus) if self.itemDataMap: self.SetColumnWidth(0, wx.LIST_AUTOSIZE) else: self.SetColumnWidth(0, 40) def UpdateText(self): pass def OnRightDown(self, evt): self.pos = evt.GetPosition() (item, flags) = self.HitTest(self.pos) if flags & wx.LIST_HITTEST_ONITEM: if not self.GetItemState(item, wx.LIST_STATE_SELECTED): self.DeselectAll() self.Select(item) evt.Skip() def OnRightUp(self, evt): if not hasattr(self, 'pos'): return None (item, flags) = self.HitTest(self.pos) if flags & wx.LIST_HITTEST_ONITEM: menu = List2Menu(self, self.menuData) self.PopupMenu(menu, self.pos) else: menu = List2Menu(self, self.menuData[2:3]) self.PopupMenu(menu, self.pos) evt.Skip() def GetSelectedEmails(self): ret = [] index = self.GetFirstSelected() while index != -1: ret.append(self.GetItem(index, 0).m_text) index = self.GetNextSelected(index) return ret class BaseHeader(BoxPanel): def __init__(self, parent, view, image_filename): BoxPanel.__init__(self, parent, wx.VERTICAL) mailicon = wx.Bitmap(image_filename, wx.BITMAP_TYPE_PNG) self.list = ClassifierList(self, mailicon) self.Add(self.list, 1, wx.EXPAND | wx.ALL, 0) self.view = view self.UpdateText() class BaseView(SplitPanel): def __init__(self, parent, model, header_class, get_messages, drag_class, klass): SplitPanel.__init__(self, parent, model) self.header = header_class(self, self) self.email = EmailView(self, model) self.Add(self.header, 1, wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, 5) self.Add(self.email, 1, wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, 5) self.SetMinimumPaneSize(60) model.AddView(self) self.UpdateText() self.get_messages = get_messages self.drag_class = drag_class self.klass = klass self.list = self.header.list self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.UpdateEmail) self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnBeginDrag) self.list.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) def UpdateText(self): self.header.UpdateText() self.email.UpdateText() def OnKillFocus(self, evt): sort = (self.list._col, self.list._colSortFlag[self.list._col]) self.model.settings.sort_spam = sort evt.Skip() def OnBeginDrag(self, evt): self.model.drag = self.drag_class d = self.get_messages() data = wx.CustomDataObject('SEDrag') mail_dict = []([ (v[3], v[5]) for k, v in d.iteritems() ]) mail_addresses = [ (mail_dict[item[3]], item) for item in self.list.GetSelectedMsgs() ] cargo = [ TYPE_EMAIL_MSGS] + mail_addresses data.SetData(cPickle.dumps(cargo, 1)) dropSource = wx.DropSource(self.list) dropSource.SetData(data) result = dropSource.DoDragDrop(wx.Drag_AllowMove) def UpdateEmail(self, evt): if not self.list.selecting: if self.list.GetSelectedMsgs() and hasattr(evt, 'm_itemIndex'): self.email.SetMsg(self.model.GetMessage(self.list.itemDataMap[self.list.GetItemData(evt.m_itemIndex)], self.klass)) else: self.email.SetMsg(self.model.GetMessage(None, self.klass)) def UpdateView(self, model, sort): if model.settings.split_vertically: self.Unsplit(self.email) self.SplitVertically(self.header, self.email, 0) else: self.Unsplit(self.email) self.SplitHorizontally(self.header, self.email, 0) mail_list = self.get_messages() (self.list._col, self.list._colSortFlag[self.list._col]) = sort update_data = { } for k, v in mail_list.iteritems(): from_status = self.model.InAddressList((v[5],))[0] if from_status: colour = None elif from_status is False: colour = None else: colour = None update_data[k] = (v[0], v[1], v[2], v[3], colour) self.list.UpdateData(update_data) self.UpdateEmail(None) def RemoveFromList(self, evt): self.model.RemoveFromList(self.list.GetSelectedMsgs(), self.klass) def MoveToHam(self, evt): msgs = self.list.GetSelectedMsgs() current = self.list.GetFocusedItem() s = self.get_messages() spam_dict = []([ (v[3], v[5]) for k, v in s.iteritems() ]) for item in msgs: if self.model.InBlacklist((spam_dict[item[3]],)).values()[0]: title = _("Warning: Address is in 'Blocked Senders'") msg = _("The message you are moving to 'Not Spam' is from an address (%s) that is in the 'Blocked Senders' list. Future email from this address will still be classified as 'spam'. Would you like to remove the address from the 'Blocked Senders' list?") % (item[0],) dlg = wx.MessageDialog(self, msg, title, wx.YES_NO) result = dlg.ShowModal() dlg.Destroy() if result == wx.ID_YES: self.model.DeleteFromBlacklist((spam_dict[item[3]],)) result == wx.ID_YES self.model.MoveToHam(self.klass, msgs) self.list.Focus(current) self.list.Select(current) self.list.EnsureVisible(current) def MoveToSpam(self, evt): msgs = self.list.GetSelectedMsgs() current = self.list.GetFocusedItem() h = self.get_messages() ham_dict = []([ (v[3], v[5]) for k, v in h.iteritems() ]) for item in msgs: if self.model.InWhitelist((ham_dict[item[3]],)).values()[0]: title = _("Warning: Address is in 'Allowed Senders'") msg = _("The message you are moving to 'Spam' is from an address (%s) that is in the 'Allowed Senders' list. Future emails from this address will still be classified as 'not spam'. Would you like to remove the address from the 'Allowed Senders' list?") % (item[0],) dlg = wx.MessageDialog(self, msg, title, wx.YES_NO) result = dlg.ShowModal() dlg.Destroy() if result == wx.ID_YES: self.model.DeleteFromWhitelist((ham_dict[item[3]],)) result == wx.ID_YES self.model.MoveToSpam(self.klass, msgs) self.list.Focus(current) self.list.Select(current) self.list.EnsureVisible(current) def MoveToUnsure(self, evt): msgs = self.list.GetSelectedMsgs() current = self.list.GetFocusedItem() h = self.get_messages() unsure_dict = []([ (v[3], v[5]) for k, v in h.iteritems() ]) self.model.MoveToUnsure(self.klass, msgs) self.list.Focus(current) self.list.Select(current) self.list.EnsureVisible(current) def AddToWhitelist(self, evt): d = self.get_messages() mail_dict = []([ (v[3], v[5]) for k, v in d.iteritems() ]) []([ mail_dict[item[3]] for item in self.list.GetSelectedMsgs() ]) def AddToBlacklist(self, evt): d = self.get_messages() mail_dict = []([ (v[3], v[5]) for k, v in d.iteritems() ]) []([ mail_dict[item[3]] for item in self.list.GetSelectedMsgs() ]) class SpamHeader(BaseHeader): def __init__(self, parent, view): BaseHeader.__init__(self, parent, view, get_image_filename('mail-icon-spam')) def UpdateText(self): self.SetHelpText(_("Emails on this page have been classified as 'spam'. If a mistake has been made please drag & drop the message to the 'Not Spam' button.")) self.list.menuData = [ (_("Move to 'Not Spam'"), self.view.MoveToHam), (_("Move to 'Unsure'"), self.view.MoveToUnsure), None, (_("Add sender to 'Blocked Senders'"), self.view.AddToBlacklist), (_("Add sender to 'Allowed Senders'"), self.view.AddToWhitelist), None, (_('Remove email(s) from list'), self.view.RemoveFromList), None, (_('Explain classification'), self.view.ShowEvidence)] self.list.UpdateText() class SpamView(BaseView): def __init__(self, parent, model): BaseView.__init__(self, parent, model, SpamHeader, model.GetSpam, 'spam', IS_SPAM) def OnFilter(self, filter_value): if filter_value != self.model.filter_spam and not (self.model.updating): self.model.filter_spam = filter_value self.model.UpdateViews() def UpdateView(self, model): sort = self.model.settings.sort_spam BaseView.UpdateView(self, model, sort) class UnsureHeader(BaseHeader): def __init__(self, parent, view): BaseHeader.__init__(self, parent, view, get_image_filename('mail-icon-unsure')) def UpdateText(self): self.SetHelpText(_("SpamExperts was unable to correctly classify the emails found on this page. Please drag & drop the emails to the appropriate 'Spam'/'Not Spam' button.")) self.list.menuData = [ (_("Move to 'Not Spam'"), self.view.MoveToHam), (_("Move to 'Spam'"), self.view.MoveToSpam), None, (_("Add sender to 'Blocked Senders'"), self.view.AddToBlacklist), (_("Add sender to 'Allowed Senders'"), self.view.AddToWhitelist), None, (_('Remove email(s) from list'), self.view.RemoveFromList), None, (_('Explain classification'), self.view.ShowEvidence)] self.list.UpdateText() class UnsureView(BaseView): def __init__(self, parent, model): BaseView.__init__(self, parent, model, UnsureHeader, model.GetUnsure, 'unsure', IS_UNSURE) def OnFilter(self, filter_value): if filter_value != self.model.filter_unsure and not (self.model.updating): self.model.filter_unsure = filter_value self.model.UpdateViews() def UpdateView(self, model): sort = self.model.settings.sort_unsure BaseView.UpdateView(self, model, sort) class HamHeader(BaseHeader): def __init__(self, parent, view): BaseHeader.__init__(self, parent, view, get_image_filename('mail-icon-ham')) def UpdateText(self): self.SetHelpText(_("Emails on this page have been classified as 'not spam'. If a mistake has been made please drag & drop the message to the 'Spam' button.")) self.list.menuData = [ (_("Move to 'Spam'"), self.view.MoveToSpam), (_("Move to 'Unsure'"), self.view.MoveToUnsure), None, (_("Add sender to 'Blocked Senders'"), self.view.AddToBlacklist), (_("Add sender to 'Allowed Senders'"), self.view.AddToWhitelist), None, (_('Remove email(s) from list'), self.view.RemoveFromList), None, (_('Explain classification'), self.view.ShowEvidence)] self.list.UpdateText() class HamView(BaseView): def __init__(self, parent, model): BaseView.__init__(self, parent, model, HamHeader, model.GetHam, 'ham', IS_HAM) def OnFilter(self, filter_value): if filter_value != self.model.filter_ham and not (self.model.updating): self.model.filter_ham = filter_value self.model.UpdateViews() def UpdateView(self, model): sort = self.model.settings.sort_ham BaseView.UpdateView(self, model, sort) class WhiteView(BoxPanel): def __init__(self, parent, model): BoxPanel.__init__(self, parent, wx.VERTICAL) help_style = wx.NO_BORDER | wx.BU_EXACTFIT self.help_button = wx.ContextHelpButton(self, style = help_style) helpicon = wx.Bitmap(get_image_filename('help-icon'), wx.BITMAP_TYPE_PNG) self.help_button.SetBitmapLabel(helpicon) self.Add(self.help_button, 0, wx.ALIGN_RIGHT | wx.ALL, 0) mailicon = wx.Bitmap(get_image_filename('mail-icon-white'), wx.BITMAP_TYPE_PNG) self.list = MailList(self, mailicon) self.Add(self.list, 1, wx.EXPAND | wx.ALL, 3) self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnBeginDrag) self.UpdateText() model.AddView(self) self.model = model def UpdateText(self): self.SetHelpText(_("On this screen you can find all senders that have been classified as 'allowed'.\n\nAny messages sent from these addresses will always be delivered to your inbox. To add an email address simply drag & drop an email from the 'Unsure', 'Not Spam' or 'Spam' list to the 'Allowed Senders' button on the left or right-click an email and select 'Move to Allowed Senders'.\n\nBy right-clicking on an empty space in the list you can manually add an email address. By right-clicking a listed email address you can remove or move it.")) self.list.menuData = [ (_("Move to 'Blocked Senders'"), self.MoveToBlacklist), None, (_('Add New Address'), self.AddAddress), None, (_('Delete'), self.Delete)] self.list.UpdateText() def OnBeginDrag(self, evt): self.model.drag = 'white' data = wx.CustomDataObject('SEDrag') cargo = [ TYPE_EMAIL_ADDRESS] + self.list.GetSelectedEmails() data.SetData(cPickle.dumps(cargo, 1)) dropSource = wx.DropSource(self.list) dropSource.SetData(data) result = dropSource.DoDragDrop(wx.Drag_AllowMove) def UpdateView(self, model): self.list.UpdateData(model.GetWhiteList()) def Delete(self, evt): self.model.DeleteFromWhitelist(self.list.GetSelectedEmails()) def MoveToBlacklist(self, evt): self.model.MoveToBlacklist(self.list.GetSelectedEmails()) def AddAddress(self, evt): dlg = wx.TextEntryDialog(self, 'Email Address', 'Add to Whitelist') if dlg.ShowModal() == wx.ID_OK: newAddress = str(dlg.GetValue()) self.model.AddEmailToWhitelist(newAddress) dlg.Destroy() class BlackView(BoxPanel): def __init__(self, parent, model): BoxPanel.__init__(self, parent, wx.VERTICAL) help_style = wx.NO_BORDER | wx.BU_EXACTFIT self.help_button = wx.ContextHelpButton(self, style = help_style) helpicon = wx.Bitmap(get_image_filename('help-icon'), wx.BITMAP_TYPE_PNG) self.help_button.SetBitmapLabel(helpicon) self.Add(self.help_button, 0, wx.ALIGN_RIGHT | wx.ALL, 0) mailicon = wx.Bitmap(get_image_filename('mail-icon-black'), wx.BITMAP_TYPE_PNG) self.list = MailList(self, mailicon) self.Add(self.list, 1, wx.EXPAND | wx.ALL, 3) self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnBeginDrag) self.UpdateText() model.AddView(self) self.model = model def UpdateText(self): self.SetHelpText(_("On this screen you can find all senders that have been classified as 'blocked'.\n\nAny messages sent from these addresses will always be blocked from arriving in your inbox. To add an email address simply drag & drop an email from the 'Unsure', 'Not Spam' or 'Spam' list to the 'Blocked Senders' button on the left or right-click an email and select 'Move to Blocked Senders'.\n\nBy right-clicking on an empty space in the list you can manually add an email address. By right-clicking a listed email address you can remove or move it.")) self.list.menuData = [ (_("Move to 'Allowed Senders'"), self.MoveToBlacklist), None, (_('Add New Address'), self.AddAddress), None, (_('Delete'), self.Delete)] self.list.UpdateText() def OnBeginDrag(self, evt): self.model.drag = 'black' data = wx.CustomDataObject('SEDrag') cargo = [ TYPE_EMAIL_ADDRESS] + self.list.GetSelectedEmails() data.SetData(cPickle.dumps(cargo, 1)) dropSource = wx.DropSource(self.list) dropSource.SetData(data) result = dropSource.DoDragDrop(wx.Drag_AllowMove) def UpdateView(self, model): self.list.UpdateData(model.GetBlackList()) def Delete(self, evt): self.model.DeleteFromBlacklist(self.list.GetSelectedEmails()) def MoveToBlacklist(self, evt): self.model.MoveToWhitelist(self.list.GetSelectedEmails()) def AddAddress(self, evt): dlg = wx.TextEntryDialog(self, 'Email Address', 'Add to Blacklist') if dlg.ShowModal() == wx.ID_OK: newAddress = str(dlg.GetValue()) self.model.AddEmailToBlacklist(newAddress) dlg.Destroy() class Classification(BoxPanel): def __init__(self, parent, model): BoxPanel.__init__(self, parent, wx.HORIZONTAL) self.model = model self.buttonBar = ButtonBar(self, model) self.spamView = SpamView(self, model) self.unsureView = UnsureView(self, model) self.hamView = HamView(self, model) self.blackView = BlackView(self, model) self.whiteView = WhiteView(self, model) self.Add(self.buttonBar, 0, wx.EXPAND) help_style = wx.NO_BORDER | wx.BU_EXACTFIT self.help_button = wx.ContextHelpButton(self, style = help_style) helpicon = wx.Bitmap(get_image_filename('help-icon'), wx.BITMAP_TYPE_PNG) self.help_button.SetBitmapLabel(helpicon) self.filter_label = wx.StaticText(self) self.filter = wx.TextCtrl(self) self.top_sizer = wx.BoxSizer(wx.HORIZONTAL) self.top_sizer.Add(self.filter_label, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 0) self.top_sizer.Add(self.filter, 1, wx.EXPAND | wx.LEFT, 2) self.top_sizer.Add(self.help_button, 0, wx.ALIGN_RIGHT | wx.ALL, 0) self.right_side = wx.BoxSizer(wx.VERTICAL) self.right_side.Add(self.top_sizer, 0, wx.EXPAND | wx.ALL, 3) self.right_side.Add(self.spamView, 1, wx.EXPAND) self.right_side.Add(self.unsureView, 1, wx.EXPAND) self.right_side.Add(self.hamView, 1, wx.EXPAND) self.right_side.Add(self.blackView, 1, wx.EXPAND) self.right_side.Add(self.whiteView, 1, wx.EXPAND) self.Add(self.right_side, 1, wx.EXPAND) self.views = (self.spamView, self.unsureView, self.hamView, self.blackView, self.whiteView) self.filters = { self.spamView: 'filter_spam', self.hamView: 'filter_ham', self.unsureView: 'filter_unsure' } self.show_top = (self.spamView, self.unsureView, self.hamView) self.active = None model.AddView(self) self.filter.Bind(wx.EVT_TEXT, self.OnFilter) def OnFilter(self, unused): active_view = self.views[self.active] active_view.OnFilter(self.filter.GetValue()) def UpdateText(self): self.filter_label.SetLabel(_('Search for:')) self.filter_label.SetBestFittingSize() self.filter_label.SetHelpText(_('Use this function to search for a specific sender and/or subject.')) self.filter.SetHelpText(_('Use this function to search for a specific sender and/or subject.')) self.buttonBar.UpdateText() self.spamView.UpdateText() self.hamView.UpdateText() self.blackView.UpdateText() self.whiteView.UpdateText() self.Layout() def UpdateView(self, model): if self.active != model.active: self.active = model.active for i, view in enumerate(self.views): if i == self.active: view.Show() if view in self.show_top: self.filter.Show() self.filter_label.Show() self.help_button.Show() else: self.filter.Hide() self.filter_label.Hide() self.help_button.Hide() view in self.show_top view.Hide() self.top_sizer.Layout() self.right_side.Layout() self.Layout() if self.views[self.active] in self.show_top: active_view = self.views[self.active] active_filter = self.filters[active_view] self.filter.SetValue(getattr(model, active_filter)) class InterceptedEdit(wx.Dialog): def __init__(self, parent, data): wx.Dialog.__init__(self, parent, title = _('Edit Ports and Description')) sizer = wx.GridBagSizer(0, 0) self.port = LeftLabeledTF(self, size = (60, -1), label = _('Ports'), validator = DigitsValidator()) self.desc = LeftLabeledTF(self, size = (160, -1), label = _('Description')) self.port.SetValue(data[1]) self.desc.SetValue(data[2]) self.ok = wx.Button(self, wx.ID_OK) self.cancel = wx.Button(self, wx.ID_CANCEL) sizer.Add(self.port, (0, 0), (1, 1)) sizer.Add(self.desc, (0, 2), (1, 3)) sizer.Add(self.ok, (1, 1), (1, 1)) sizer.Add(self.cancel, (1, 2), (1, 1)) self.SetSizerAndFit(sizer) def UpdateText(self): pass class InterceptedApps(BoxPanel): def __init__(self, parent, model): BoxPanel.__init__(self, parent) self.add = wx.Button(self, label = '') self.remove = wx.Button(self, label = '') self.remove.Enable(False) self.list = AutoWidthSortedLC(self, style = wx.LC_SINGLE_SEL | wx.LC_REPORT | wx.LC_SORT_ASCENDING | wx.LC_VRULES | wx.LC_HRULES) self.Add(self.list, 2, wx.EXPAND | wx.ALL, 5) rSizer = wx.BoxSizer(wx.HORIZONTAL) rSizer.Add(self.add, 0, wx.ALL, 5) rSizer.Add(self.remove, 0, wx.ALL, 5) self.Add(rSizer, 0, wx.ALIGN_RIGHT, 5) self.add.Bind(wx.EVT_BUTTON, self.OnAdd) self.remove.Bind(wx.EVT_BUTTON, self.OnRemove) self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelect) self.list.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnSelect) self.list.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnActivate) self.UpdateView(model) self.UpdateText() model.AddView(self) self.model = model def UpdateText(self): self.SetTitle(_('Intercepted email clients')) self.add.SetTitle(_('Add')) self.add.SetBestFittingSize() self.remove.SetTitle(_('Remove')) self.remove.SetBestFittingSize() self.SetHelpText(_('Intercepted email clients is a list of executables that will be automatically intercepted by SpamExperts.\n\nIf you would like to exclude an email client from being filtered, please remove it from the list. If your email client is not in the list, please add the executable.')) def OnActivate(self, evt): old = self.list.itemDataMap[self.list.GetItemData(evt.m_itemIndex)] dlg = InterceptedEdit(self, old) if dlg.ShowModal() == wx.ID_OK: new = [ old[0], dlg.port.GetValue(), dlg.desc.GetValue()] if old != new: self.GetParent().GetParent().apply.Enable(True) index = self.list.GetItemData(evt.m_itemIndex) self.list.itemDataMap[index] = (new[0], new[1], new[2]) self.UpdateData(self.list.itemDataMap) dlg.Destroy() def OnSelect(self, evt): index = self.list.GetFirstSelected() self.remove.Enable(index != -1) def OnAdd(self, evt): wildcardSpec = 'Executable files (*.exe)|*.exe' dialog = wx.FileDialog(self, _('Select email client executable to intercept'), wildcard = wildcardSpec, style = wx.OPEN) if dialog.ShowModal() == wx.ID_OK: path = dialog.GetPath() data = self.model.GetInterceptedAppData(path) if data not in self.list.itemDataMap.values(): key = max(self.list.itemDataMap.keys()) + 1 self.list.itemDataMap[key] = data index = self.list.InsertImageStringItem(sys.maxint, data[0], 2) self.list.SetStringItem(index, 1, data[1]) self.list.SetStringItem(index, 2, data[2]) self.list.SetItemData(index, key) self.GetParent().GetParent().apply.Enable(True) dialog.Destroy() def OnRemove(self, evt): index = self.list.GetFirstSelected() if index != -1: sel = self.list.GetItemData(index) self.list.DeleteItem(index) self.list.itemDataMap.pop(sel) self.GetParent().GetParent().apply.Enable(True) def UpdateView(self, model): data = model.settings.mailapps app_dict = dict(enumerate(data)) self.UpdateData(app_dict) def UpdateData(self, data): self.columns = [ (_('Executable'), 150), (_('Port'), 60), (_('Application Name'), 250)] self.list.itemDataMap = data self.list.ClearAll() self.ResetColumns() for key, data in self.list.itemDataMap.items(): index = self.list.InsertImageStringItem(sys.maxint, data[0], 2) self.list.SetStringItem(index, 1, str(data[1])) self.list.SetStringItem(index, 2, data[2]) self.list.SetItemData(index, key) for i in xrange(3): self.list.SetColumnWidth(i, wx.LIST_AUTOSIZE) def ResetColumns(self): for column, width in self.columns: idx = self.columns.index((column, width)) self.list.InsertColumn(idx, column) def GetValue(self): return self.list.itemDataMap.values() class PeriodicRetrieval(BoxPanel): def __init__(self, parent, model): wx.Panel.__init__(self, parent) style = wx.LC_SINGLE_SEL | wx.LC_REPORT | wx.LC_SORT_ASCENDING | wx.LC_VRULES | wx.LC_HRULES self.model = model self.initial_dns_lookup = False self.list_box = wx.StaticBox(self, -1, '') self.list_box_sizer = wx.StaticBoxSizer(self.list_box, wx.VERTICAL) self.list = AutoWidthSortedLC(self, style = style) self.list_box_sizer.Add(self.list, 2, wx.EXPAND | wx.ALL, 5) self.remove = wx.Button(self, label = '') self.remove.Enable(False) self.list_box_sizer.Add(self.remove, 0, wx.ALIGN_RIGHT, 10) self.not_list_box = wx.StaticBox(self, -1, '') self.not_list_box_sizer = wx.StaticBoxSizer(self.not_list_box, wx.VERTICAL) self.not_list = AutoWidthSortedLC(self, style = style) self.not_list_box_sizer.Add(self.not_list, 2, wx.EXPAND | wx.ALL, 5) self.not_remove = wx.Button(self, label = '') self.not_remove.Enable(False) self.not_list_box_sizer.Add(self.not_remove, 0, wx.ALIGN_RIGHT, 10) border = wx.BoxSizer(wx.VERTICAL) border.Add(self.list_box_sizer, 1, wx.EXPAND | wx.ALL, 5) border.Add(self.not_list_box_sizer, 1, wx.EXPAND | wx.ALL, 5) self.SetSizer(border) self.UpdateView(model) self.UpdateText() model.AddView(self) self.remove.Bind(wx.EVT_BUTTON, self.OnRemove) self.not_remove.Bind(wx.EVT_BUTTON, self.OnNotRemove) self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelect) self.list.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnSelect) self.list.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnBeginPeriodicDrag) self.not_list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnNotSelect) self.not_list.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnNotSelect) self.not_list.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnBeginNotPeriodicDrag) self.list.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown) self.list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) self.list.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) self.not_list.Bind(wx.EVT_RIGHT_DOWN, self.OnNotRightDown) self.not_list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnNotRightUp) self.not_list.Bind(wx.EVT_RIGHT_UP, self.OnNotRightUp) self.target = ListDropTarget(self.OnDropPeriod) self.not_target = ListDropTarget(self.OnDropNotPeriod) self.list.SetDropTarget(self.target) self.not_list.SetDropTarget(self.not_target) self.not_list.menuData = [ (_('Check Periodically'), self.MoveToPeriodic), (_('Remove'), self.OnNotRemove)] self.list.menuData = [ (_("Don't Check Periodically"), self.MoveToNotPeriodic), (_('Remove'), self.OnRemove)] def MoveToPeriodic(self, unused): index = self.not_list.GetFirstSelected() if index != -1: cargo = ((index, self.not_list.itemDataMap[self.not_list.GetItemData(index)]),) self.OnDropPeriod(cargo) def MoveToNotPeriodic(self, unused): index = self.list.GetFirstSelected() if index != -1: cargo = ((index, self.list.itemDataMap[self.list.GetItemData(index)]),) self.OnDropNotPeriod(cargo) def RightDown(self, evt, ctrl): self.pos = evt.GetPosition() (item, flags) = ctrl.HitTest(self.pos) if flags & wx.LIST_HITTEST_ONITEM: if not ctrl.GetItemState(item, wx.LIST_STATE_SELECTED): ctrl.DeselectAll() ctrl.Select(item) evt.Skip() def RightUp(self, evt, ctrl): (item, flags) = ctrl.HitTest(self.pos) if flags & wx.LIST_HITTEST_ONITEM: menu = List2Menu(ctrl, ctrl.menuData) ctrl.PopupMenu(menu, self.pos) evt.Skip() def OnRightDown(self, evt): self.RightDown(evt, self.list) def OnRightUp(self, evt): self.RightUp(evt, self.list) def OnNotRightDown(self, evt): self.RightDown(evt, self.not_list) def OnNotRightUp(self, evt): self.RightUp(evt, self.not_list) def OnBeginDrag(self, list_control): data = wx.CustomDataObject('SEListDrag') cargo = [] index = list_control.GetFirstSelected() while index != -1: cargo.append((index, list_control.itemDataMap[list_control.GetItemData(index)])) index = list_control.GetNextSelected(index) data.SetData(cPickle.dumps(cargo, cPickle.HIGHEST_PROTOCOL)) dropSource = wx.DropSource(list_control) dropSource.SetData(data) dropSource.DoDragDrop(wx.Drag_AllowMove) def OnBeginPeriodicDrag(self, evt): self.not_target.match = True self.target.match = False self.OnBeginDrag(self.list) def OnBeginNotPeriodicDrag(self, evt): self.target.match = True self.not_target.match = False self.OnBeginDrag(self.not_list) def OnDrop(self, cargo, from_, to): for index, data in cargo: sel = from_.GetItemData(index) from_.DeleteItem(index) from_.itemDataMap.pop(sel) host = dnslookup.lookup[data[0]] index = to.InsertImageStringItem(sys.maxint, host, 2) to.SetStringItem(index, 1, data[2]) key = len(to.itemDataMap) to.SetItemData(index, key) to.itemDataMap[key] = data self.GetParent().GetParent().apply.Enable(True) def OnDropPeriod(self, cargo): cargo = [ (i, c[:3] + ('',) + c[-2:]) for i, c in cargo ] self.OnDrop(cargo, self.not_list, self.list) def OnDropNotPeriod(self, cargo): cargo = [ (i, c[:3] + c[-2:]) for i, c in cargo ] self.OnDrop(cargo, self.list, self.not_list) def UpdateText(self): self.list_box.SetTitle(_('Connections to retrieve mail from')) self.not_list_box.SetTitle(_('Connections to not retrieve mail from')) self.remove.SetTitle(_('Remove')) self.remove.SetBestFittingSize() self.not_remove.SetTitle(_('Remove')) self.not_remove.SetBestFittingSize() self.SetHelpText(_("When 'Periodic retrieval' is enabled, SpamExperts will automatically check for new emails at the set interval. Whenever 'not spam' emails have been retrieved, it will inform you.")) def OnSelect(self, evt): index = self.list.GetFirstSelected() self.remove.Enable(index != -1) def OnNotSelect(self, evt): index = self.not_list.GetFirstSelected() self.not_remove.Enable(index != -1) def OnRemove(self, evt): index = self.list.GetFirstSelected() if index != -1: sel = self.list.GetItemData(index) self.list.DeleteItem(index) self.list.itemDataMap.pop(sel) self.GetParent().GetParent().apply.Enable(True) def OnNotRemove(self, evt): index = self.not_list.GetFirstSelected() if index != -1: sel = self.not_list.GetItemData(index) self.not_list.DeleteItem(index) self.not_list.itemDataMap.pop(sel) self.GetParent().GetParent().apply.Enable(True) def UpdateView(self, model): remove_dict = dict(enumerate(model.settings.periodic)) not_dict = dict(enumerate(model.settings.not_periodic)) self.UpdateData(remove_dict, not_dict) def UpdateData(self, data, not_data): self.columns = [ (_('Server'), 250), (_('Username'), 150)] self.list.itemDataMap = data self.list.ClearAll() self.not_list.itemDataMap = not_data self.not_list.ClearAll() self.ResetColumns() for ctrl in (self.list, self.not_list): for key, data in ctrl.itemDataMap.items(): if self.initial_dns_lookup: d = dnslookup.lookup[data[0]] else: d = data[0] index = ctrl.InsertImageStringItem(sys.maxint, d, 2) ctrl.SetStringItem(index, 1, data[2]) ctrl.SetItemData(index, key) if ctrl.itemDataMap: for i in xrange(2): ctrl.SetColumnWidth(i, wx.LIST_AUTOSIZE) for unused, size in enumerate(self.columns): ctrl.SetColumnWidth(i, size) if not self.initial_dns_lookup: t = threading.Thread(target = self.FillInDNS) t.start() def FillInDNS(self): if self.initial_dns_lookup: return None dns = { } for ctrl in (self.list, self.not_list): for key, d in ctrl.itemDataMap.items(): dnslookup.lookup[d[0]] self.initial_dns_lookup = True def ResetColumns(self): for title, unused in enumerate(self.columns): self.list.InsertColumn(i, title) self.not_list.InsertColumn(i, title) def GetValue(self): return (self.list.itemDataMap.values(), self.not_list.itemDataMap.values()) class Settings(BoxPanel): def __init__(self, parent, model, tray): BoxPanel.__init__(self, parent) self.block = wx.CheckBox(self, label = '') self.modSubj = wx.CheckBox(self, label = '') self.tag = wx.TextCtrl(self) self.lang = LeftLabeledChoice(self, '', choices = tray.languages.values()) try: language = tray.languages[model.settings.lang] except KeyError: print >>sys.stderr, 'Unknown language,', model.settings.lang, '(using English).' language = 'English' self.lang.SetStringSelection(language) self.date_header = LeftLabeledChoice(self, '', choices = ('Delivery-Date', 'Date')) self.date_header.SetStringSelection(model.GetSettings().date_header) self.on_startup = wx.CheckBox(self, label = '') self.vertical = wx.CheckBox(self, label = '') self.user_address = wx.TextCtrl(self) self.auto_bugreport = wx.CheckBox(self, label = '') self.auto_update = wx.CheckBox(self, label = '') self.browse = wx.Button(self, label = '') self.notify_sound = LeftLabeledTF(self, '') self.cache_expiry_days = LeftLabeledTF(self, size = (40, -1), validator = DigitValidator()) self.enable_period = wx.CheckBox(self, label = '') self.period = wx.TextCtrl(self, size = (40, -1), validator = DigitValidator()) self.enable_balloons = wx.CheckBox(self, label = '') self.sizers = [ self.GetSizer()] self.Add(self.block, 0, wx.ALIGN_LEFT | wx.ALL, 5) self.subj_sizer = wx.BoxSizer(wx.HORIZONTAL) self.subj_sizer.Add(self.modSubj, 0, wx.ALIGN_CENTRE | wx.ALIGN_LEFT | wx.ALL, 0) self.subj_sizer.Add(self.tag, 1, wx.ALL | wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_RIGHT, 0) self.sizers.append(self.subj_sizer) self.Add(self.subj_sizer, 1, wx.EXPAND | wx.ALIGN_LEFT | wx.ALL, 5) self.Add(self.on_startup, 0, wx.ALIGN_LEFT | wx.ALL, 5) self.Add(self.auto_update, 0, wx.ALIGN_LEFT | wx.ALL, 5) self.report_sizer = wx.BoxSizer(wx.HORIZONTAL) self.report_sizer.Add(self.auto_bugreport, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT, 0) flags = wx.TOP | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL self.report_sizer.Add(self.user_address, 1, flags, 2) self.sizers.append(self.report_sizer) self.Add(self.report_sizer, 1, wx.EXPAND | wx.ALIGN_LEFT | wx.ALL, 5) self.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 3) self.Add(self.vertical, 0, wx.ALIGN_LEFT | wx.ALL, 5) self.Add(self.lang, 0, wx.ALIGN_LEFT | wx.ALL, 5) self.Add(self.date_header, 0, wx.ALIGN_LEFT | wx.ALL, 5) self.sound = wx.BoxSizer(wx.HORIZONTAL) self.sound.Add(self.notify_sound, 1, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND | wx.ALIGN_LEFT | wx.ALL, 0) self.sound.Add(self.browse, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT, 2) self.sizers.append(self.sound) self.Add(self.sound, 1, wx.EXPAND | wx.ALIGN_LEFT | wx.ALL, 5) self.Add(self.cache_expiry_days, 0, wx.ALIGN_LEFT | wx.ALL, 5) self.period_sizer = wx.BoxSizer(wx.HORIZONTAL) self.period_sizer.Add(self.enable_period, 0, wx.ALIGN_CENTER | wx.ALIGN_LEFT | wx.ALL, 0) self.period_sizer.Add(self.period, 0, wx.ALIGN_CENTER | wx.ALIGN_RIGHT | wx.ALL, 0) self.sizers.append(self.period_sizer) self.Add(self.period_sizer, 0, wx.ALIGN_LEFT | wx.ALL, 5) self.Add(self.enable_balloons, 0, wx.ALIGN_LEFT | wx.ALL, 5) self.block.Bind(wx.EVT_CHECKBOX, self.UpdateApply) self.modSubj.Bind(wx.EVT_CHECKBOX, self.UpdateApply) self.tag.Bind(wx.EVT_TEXT, self.UpdateApply) self.cache_expiry_days.tf.Bind(wx.EVT_TEXT, self.UpdateApply) self.lang.ch.Bind(wx.EVT_CHOICE, self.UpdateApply) self.date_header.ch.Bind(wx.EVT_CHOICE, self.UpdateApply) self.auto_bugreport.Bind(wx.EVT_CHECKBOX, self.UpdateApply) self.auto_update.Bind(wx.EVT_CHECKBOX, self.UpdateApply) self.user_address.Bind(wx.EVT_TEXT, self.UpdateApply) self.on_startup.Bind(wx.EVT_CHECKBOX, self.UpdateApply) self.vertical.Bind(wx.EVT_CHECKBOX, self.UpdateApply) self.browse.Bind(wx.EVT_BUTTON, self.OnBrowse) self.period.Bind(wx.EVT_TEXT, self.UpdateApply) self.notify_sound.tf.Bind(wx.EVT_TEXT, self.UpdateApply) self.enable_period.Bind(wx.EVT_CHECKBOX, self.UpdateApply) self.enable_balloons.Bind(wx.EVT_CHECKBOX, self.UpdateApply) self.model = model self.tray = tray self.UpdateText() model.AddView(self) def UpdateText(self): self.block.SetLabel(_('Block Spam')) self.block.SetBestFittingSize() self.block.SetHelpText(_("When 'Block Spam' has been selected, email recognized as 'spam' will not arrive in your email client. Note that you will have to check your email twice from your email client in order to receive the filtered email. Every check is always one round behind so the 'spam' can be filtered and the email can be prepared to be released to the email client. When using 'Periodic Retrieval' this will be handled at the background.")) self.modSubj.SetLabel(_('Modify subject, using tag')) self.modSubj.SetBestFittingSize() self.modSubj.SetHelpText(_("Please select this option to modify the subject of emails detected as 'spam' using the tag on the right.")) self.tag.SetBestFittingSize() self.tag.SetHelpText(_("When 'Modify Subject' has been selected, the text specified on the right will be added to the subject of email recognized as 'spam'. This way you can easily recognize what has been detected as 'spam', and you can create a rule in your email client to move the detected email to a specified folder based on its subject.")) self.lang.SetLabel(_('Language:')) self.lang.SetBestFittingSize() self.notify_sound.SetLabel(_('New email sound notification: ')) self.notify_sound.SetBestFittingSize() self.notify_sound.SetHelpText(_("SpamExperts can play a sound when new 'not spam' email has arrived.")) self.cache_expiry_days.SetLabel(_('Cache expiry time (days)')) self.cache_expiry_days.SetBestFittingSize() self.cache_expiry_days.SetHelpText(_('Here you can set the amount of days after which SpamExperts will automatically delete the emails from its internal cache. This does not influence the training.')) self.browse.SetTitle(_('Browse')) self.browse.SetBestFittingSize() self.auto_update.SetLabel(_('Automatically update')) self.auto_update.SetBestFittingSize() self.auto_update.SetHelpText(_('This will automatically update SpamExperts whenever an update is available.')) self.auto_bugreport.SetLabel(_('Automatically send bug reports from email address')) self.auto_bugreport.SetBestFittingSize() self.auto_bugreport.SetHelpText(_('This will allow SpamExperts to automatically send a bug report when a crash occurs.')) self.user_address.SetBestFittingSize() self.user_address.SetHelpText(_('Please enter your email address so we will be able to contact you with a possible solution.')) self.date_header.SetLabel(_('Date sorting type:')) self.date_header.SetBestFittingSize() self.date_header.SetHelpText(_('Different email clients use a different date sorting method. When the SpamExperts date format does not match your email client please choose the alternative sorting format.')) self.on_startup.SetLabel(_('Launch SpamExperts on login')) self.on_startup.SetBestFittingSize() self.on_startup.SetHelpText(_('Automatically start SpamExperts when Windows is loaded.')) self.vertical.SetLabel(_('Display email list and content side-by-side')) self.vertical.SetBestFittingSize() self.vertical.SetHelpText(_('This option allows you to switch between a side-by-side or a top-bottom display of the email list and their contents.')) self.enable_period.SetLabel(_('Periodically check for email every (minutes)')) self.enable_period.SetBestFittingSize() self.enable_period.SetHelpText(_("When 'Periodic retrieval' is enabled, SpamExperts will automatically check for new emails at the set interval. Whenever 'not spam' emails have been retrieved, it will inform you.")) self.period.SetHelpText(_("When 'Periodic retrieval' is enabled, SpamExperts will automatically check for new emails at the set interval. Whenever 'not spam' emails have been retrieved, it will inform you.")) self.enable_balloons.SetLabel(_('Display balloon notifications')) self.enable_balloons.SetBestFittingSize() self.enable_balloons.SetHelpText(_("When there is new 'not spam' email, SpamExperts can inform you with a balloon notification.")) self.AdjustReportSizer() for sizer in self.sizers: sizer.Layout() def AdjustReportSizer(self): self.report_sizer.Layout() if self.report_sizer.GetOrientation() == wx.HORIZONTAL: if self.user_address.GetSize()[0] < self.user_address.GetSize()[0]: pass elif self.user_address.GetSize()[0] < 100: self.report_sizer.SetOrientation(wx.VERTICAL) self.report_sizer.Remove(1) flags = wx.TOP | wx.EXPAND | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL self.report_sizer.Add(self.user_address, 0, flags, 2) else: self.report_sizer.SetOrientation(wx.HORIZONTAL) self.report_sizer.Remove(1) flags = wx.TOP | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL self.report_sizer.Add(self.user_address, 1, flags, 2) self.GetSizer().Fit(self) grandparent = self.GetParent().GetParent() grandparent.GetSizer().Fit(grandparent) def UpdateView(self, model): settings = model.GetSettings() self.block.SetValue(settings.block_spam) self.modSubj.SetValue(settings.modify_subject) self.tag.SetValue(settings.modify_subject_tag) self.user_address.SetValue(settings.user_address) self.auto_bugreport.SetValue(settings.auto_bugreport) self.auto_update.SetValue(settings.automatically_update) self.on_startup.SetValue(settings.launch_on_startup) self.vertical.SetValue(settings.split_vertically) self.notify_sound.SetValue(settings.notify_sound) self.cache_expiry_days.SetValue(str(options[('Storage', 'cache_expiry_days')])) self.enable_period.SetValue(model.settings.enable_periodic) self.period.SetValue(str(model.settings.period)) self.enable_balloons.SetValue(settings.enable_balloon_notifications) def OnBrowse(self, evt): wildcard = 'Wave files (*.wav)|*.wav|All files (*.*)|*.*' default_dir = os.path.join(application_directory(), 'sounds') dlg = wx.FileDialog(self, message = 'Choose a sound file', defaultDir = default_dir, defaultFile = '', wildcard = wildcard, style = wx.OPEN) if dlg.ShowModal() == wx.ID_OK: paths = dlg.GetPaths() self.notify_sound.SetValue(paths[0]) dlg.Destroy() def UpdateApply(self, unused): apply = self.GetParent().GetParent().apply if self.cache_expiry_days.GetValue() != str(options[('Storage', 'cache_expiry_days')]): apply.Enable(True) return None settings = [ (self.block.GetValue(), 'block_spam'), (self.modSubj.GetValue(), 'modify_subject'), (self.tag.GetValue(), 'modify_subject_tag'), (self.auto_bugreport.GetValue(), 'auto_bugreport'), (self.user_address.GetValue(), 'user_address'), (self.date_header.GetSelection(), 'date_header'), (self.auto_update.GetValue(), 'automatically_update'), (self.on_startup.GetValue(), 'launch_on_startup'), (self.vertical.GetValue(), 'split_vertically'), (self.enable_period.GetValue(), 'enable_periodic'), (self.notify_sound.GetValue(), 'notify_sound'), (self.enable_balloons.GetValue(), 'enable_balloon_notifications')] try: period = float(self.period.GetValue()) except ValueError: period = self.period.GetValue() settings.append((period, 'period')) for code, name in self.tray.languages.iteritems(): if name == self.lang.GetSelection(): settings.append((code, 'lang')) break continue old_settings = self.model.settings apply.Enable(False) for new_setting, att in settings: if getattr(old_settings, att) != new_setting: apply.Enable(True) break continue class LSPStatus(BoxPanel): def __init__(self, parent, model): BoxPanel.__init__(self, parent, wx.HORIZONTAL) self.stat = wx.StaticText(self) self.status_label = wx.StaticText(self, label = '') self.Add(self.status_label, 0, wx.ALIGN_CENTER) self.Add(self.stat) self.UpdateText() model.AddView(self) def UpdateText(self): self.SetHelpText(_("When this status is 'On', the LSP is able to intercept email connections.")) self.status_label.SetLabel(_('LSP Status: ')) self.status_label.SetBestFittingSize() def UpdateView(self, model): if model.lsp_on: self.stat.SetLabel(_('ON')) self.stat.SetForegroundColour('black') else: self.stat.SetLabel(_('OFF')) self.stat.SetForegroundColour('red') self.GetParent().GetSizer().Layout() class AboutSE(BoxPanel): def __init__(self, parent, model): BoxPanel.__init__(self, parent) self.copyright = wx.StaticText(self, label = '', style = wx.ALIGN_CENTER) self.model = model self.credits = wx.StaticText(self, label = '', style = wx.ALIGN_CENTER) self.translation_credits = wx.StaticText(self, label = '', style = wx.ALIGN_CENTER) self.Add((-1, 10)) self.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 20) self.Add(self.credits, 0, wx.ALIGN_CENTER) self.Add((-1, 10)) self.Add(self.translation_credits, 0, wx.ALIGN_CENTER) self.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 20) self.Add(self.copyright, 0, wx.ALIGN_CENTER) self.Add((-1, 10)) self.support = LabeledHyperlink(self, '', 'mailto:support@spamexperts.com', 'mailto:support@spamexperts.com') self.Add(self.support, 0, wx.ALIGN_CENTER) self.website = LabeledHyperlink(self, '', 'http://www.spamexperts.com', 'http://www.spamexperts.com') self.Add(self.website, 0, wx.ALIGN_CENTER) self.UpdateText() def UpdateText(self): copyright = _('Copyright (C) 2006 SpamExperts V.O.F. All rights reserved.') self.copyright.SetLabel(copyright) self.copyright.SetBestFittingSize() credits = _('Programming credits to:\nPaul Boots, Peter Damoc, Paul Jansen,\nNenad Nikolic, Barak Weichselbaum, Stuart Welch, Tony Meyer') self.credits.SetLabel(credits) self.credits.SetBestFittingSize() translation = _('Translation credits to:\nSergey Burkov, Krzysztof Zielinski, Andrea Chavarro Manotas') self.translation_credits.SetLabel(translation) self.translation_credits.SetBestFittingSize() self.support.SetLabel(_('Support: ')) self.support.SetBestFittingSize() self.website.SetLabel(_('Website: ')) self.website.SetBestFittingSize() class Status(BoxPanel): def __init__(self, parent, model): BoxPanel.__init__(self, parent) self.lsp_status = LSPStatus(self, model) self.model = model self.stats = wx.StaticBox(self) self.real_stats_sizer = wx.StaticBoxSizer(self.stats, wx.VERTICAL) self.stats_sizer = wx.BoxSizer(wx.VERTICAL) self.real_stats_sizer.Add(self.stats_sizer, 1, wx.EXPAND) self.Add(self.lsp_status, 0, wx.ALIGN_CENTER) self.sizer.AddSpacer((-1, 10)) self.Add(self.real_stats_sizer, 1, wx.ALIGN_CENTER | wx.EXPAND) self.reset_stats = wx.Button(self) self.reset_stats.Bind(wx.EVT_BUTTON, self.OnReset) self.controls = [] self.UpdateText() def UpdateText(self): self.reset_stats.SetLabel(_('Reset statistics')) self.reset_stats.SetBestFittingSize() self.reset_stats.SetHelpText(_('Resetting statistics does not influence any training data.')) self.stats.SetLabel(_('Performance Statistics')) while True: try: self.stats_sizer.Detach(0) continue except wx.PyAssertionError: break continue None<EXCEPTION MATCH>wx.PyAssertionError for control in self.controls: try: control.Hide() except TypeError: pass control.Destroy() self.controls = [] if hasattr(self.model.state, 'statistics'): stats = self.model.state.statistics.GetStats() else: print >>sys.stderr, "Can't display statistics before starting up." stats = () for stat, help in stats: if help == help: pass elif help == '': control = wx.StaticLine(self) else: control = wx.BoxSizer(wx.HORIZONTAL) parts = stat.split('\t') for i, part in enumerate(parts): part_control = wx.StaticText(self, label = part) part_control.SetHelpText(help) part_control.SetBestFittingSize() if i == 0: align = wx.ALIGN_LEFT elif i == len(parts) - 1: align = wx.ALIGN_RIGHT else: align = wx.ALIGN_CENTER control.Add(part_control, 1, wx.EXPAND | align) self.stats_sizer.Add(control, 0, wx.EXPAND | wx.ALL, 3) self.controls.append(control) self.stats_sizer.AddSpacer((-1, 10)) self.stats_sizer.Add(self.reset_stats, 0, wx.ALIGN_CENTER | wx.ALL, 3) self.stats.SetHelpText(_('Information about how well SpamExperts is performing.')) self.stats_sizer.Layout() self.sizer.Layout() self.SetBestFittingSize() self.stats.Refresh() self.stats.Update() self.Refresh() self.Update() self.lsp_status.UpdateText() def OnReset(self, unused): if options[('globals', 'verbose')]: print 'Resetting performance statistics' if hasattr(self.model.state, 'statistics'): self.model.state.statistics.Reset() self.model.state.statistics.ResetTotal(True) else: print >>sys.stderr, "Can't reset stats before starting up." self.UpdateText() self.GetParent().SetBestFittingSize() class ProCodeDialog(wx.Dialog): def __init__(self, parent, ID, title, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE): wx.Dialog.__init__(self, parent, ID, title, pos, size, style) sizer = wx.BoxSizer(wx.VERTICAL) label = wx.StaticText(self, -1, _('Please enter your code:')) sizer.Add(label, 0, wx.ALIGN_LEFT | wx.ALL, 5) box = wx.BoxSizer(wx.HORIZONTAL) self.one = wx.TextCtrl(self, -1, '', size = (45, -1)) self.one.Bind(wx.EVT_TEXT, (lambda x: self.MoveOn(self.one, self.two))) box.Add(self.one, 1, wx.ALIGN_CENTRE | wx.ALL, 5) self.two = wx.TextCtrl(self, -1, '', size = (45, -1)) self.two.Bind(wx.EVT_TEXT, (lambda x: self.MoveOn(self.two, self.three))) box.Add(self.two, 1, wx.ALIGN_CENTRE | wx.ALL, 5) self.three = wx.TextCtrl(self, -1, '', size = (45, -1)) self.three.Bind(wx.EVT_TEXT, (lambda x: self.MoveOn(self.three, self.four))) box.Add(self.three, 1, wx.ALIGN_CENTRE | wx.ALL, 5) self.four = wx.TextCtrl(self, -1, '', size = (45, -1)) self.four.SetMaxLength(5) box.Add(self.four, 1, wx.ALIGN_CENTRE | wx.ALL, 5) sizer.Add(box, 0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) line = wx.StaticLine(self, -1, size = (20, -1), style = wx.LI_HORIZONTAL) sizer.Add(line, 0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.TOP, 5) btnsizer = wx.StdDialogButtonSizer() btn = wx.Button(self, wx.ID_OK) btn.SetDefault() btnsizer.AddButton(btn) btn = wx.Button(self, wx.ID_CANCEL) btnsizer.AddButton(btn) btnsizer.Realize() sizer.Add(btnsizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.ALL, 5) self.SetSizer(sizer) sizer.Fit(self) def MoveOn(self, cur, next): if len(cur.GetValue()) >= 5: next.SetFocus() if len(cur.GetValue()) > 5: next.SetValue(cur.GetValue()[5:].replace('-', '')) cur.SetValue(cur.GetValue()[:5]) if len(self.four.GetValue()) == 5: self.four.SetFocus() self.four.SetInsertionPoint(5) def Code(self): return '-'.join((self.one.GetValue(), self.two.GetValue(), self.three.GetValue(), self.four.GetValue())) class WelcomeDialog(wx.Dialog): def __init__(self, parent, settings): title = _('Welcome to SpamExperts') pre = wx.PreDialog() pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) pre.Create(None, -1, title = title) self.PostCreate(pre) self.settings = settings sizer = wx.BoxSizer(wx.VERTICAL) important = wx.StaticText(self, -1, _('Important')) try: font = wx.Font(-1, wx.DEFAULT, wx.NORMAL, wx.BOLD) except wx.PyAssertionError: font = wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.BOLD) important.SetFont(font) training_msg = _("In order to effectively use this software, and enable its self-learning capabilities, we recommend you correctly classify a minimum of 10 'spam' and 10 'not spam' messages.\n\nAll incoming messages will be displayed in the 'Spam', 'Not Spam' or 'Unsure' list. You should always move the messages from the 'Unsure' list to the appropriate 'Spam' or 'Not Spam' button. Whenever SpamExperts has made a classification mistake, you should correct this by moving the message to the appropriate button.\n\nIf you have any questions about this software feel free to contact us.") training_msg = fill(training_msg, 80) training = wx.StaticText(self, -1, training_msg) periodic_msg = _("When 'periodic retrieval' is enabled, SpamExperts will automatically check your account(s) for new email (default every 5 minutes). Whenever 'not spam' email has been retrieved you will be informed.") periodic_msg = fill(periodic_msg, 80) periodic = wx.StaticText(self, -1, periodic_msg) enable_periodic = wx.CheckBox(self, -1, _('Enable periodic retrieval')) self.enable_periodic = enable_periodic enable_periodic.SetValue(self.settings.enable_periodic) enable_periodic.Bind(wx.EVT_CHECKBOX, self.on_enable_periodic) enable_periodic.SetHelpText(_("When 'periodic retrieval' is enabled, SpamExperts will automatically check your account(s) for new email (default every 5 minutes). Whenever 'not spam' email has been retrieved you will be informed.")) show = wx.CheckBox(self, -1, _('Show this dialog on startup')) show.SetValue(self.settings.show_welcome) show.Bind(wx.EVT_CHECKBOX, self.on_show_on_startup) show.SetHelpText(_('If you untick this box, this dialog will no longer display when SpamExperts starts.')) self.SetBackgroundColour('white') training_box = wx.StaticBox(self, -1, _('Training Tip')) training_box_sizer = wx.StaticBoxSizer(training_box, wx.VERTICAL) periodic_box = wx.StaticBox(self, -1, _('Periodic Retrieval')) periodic_box_sizer = wx.StaticBoxSizer(periodic_box, wx.VERTICAL) training_box_sizer.Add(important, 0, wx.ALIGN_CENTER | wx.ALL, 5) training_box_sizer.Add(training, 0, wx.ALIGN_LEFT | wx.ALL, 5) periodic_box_sizer.Add(periodic, 0, wx.ALIGN_LEFT | wx.ALL, 5) periodic_box_sizer.Add(enable_periodic, 0, wx.ALIGN_LEFT | wx.ALL, 5) sizer.Add(training_box_sizer, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, 5) sizer.Add(periodic_box_sizer, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, 5) sizer.Add(show, 0, wx.ALIGN_RIGHT | wx.ALL, 10) training_box.SetFocus() self.SetSizer(sizer) sizer.Fit(self) def on_show_on_startup(self, unused): self.settings.show_welcome = not (self.settings.show_welcome) def on_enable_periodic(self, unused): self.settings.enable_periodic = self.enable_periodic.GetValue() class MainFrame(wx.Frame): CLOSE_ID = 101 EXIT_ID = 102 CONFIGURE_ID = 201 RESET_ID = 202 ABOUT_ID = 301 STATUS_ID = 302 SUBMIT_ID = 303 PROFESSIONAL_ID = 304 CHECK_ID = 305 FAQ_ID = 306 INSTRUCTIONS_ID = 307 UPGRADE_ID = 308 def __init__(self, model, tray): if license.get_code(): title = _('SpamExperts Professional') else: title = _('SpamExperts Home') wx.Frame.__init__(self, None, -1, title = title, style = wx.CAPTION | wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.RESIZE_BORDER | wx.MAXIMIZE_BOX | wx.CLOSE_BOX) self.SetBackgroundColour('white') self.SetMinSize((100, 250)) sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) logo = wx.Image(get_image_filename('logo', tray.lang)).ConvertToBitmap() self.logo = wx.StaticBitmap(self, bitmap = logo) sizer.Add(self.logo, 0, wx.ALIGN_CENTER | wx.ALL, 3) sizer.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 0) self.cls = Classification(self, model) sizer.Add(self.cls, 2, wx.EXPAND | wx.ALL, 3) self.Bind(EVT_SUBMIT_REPORT, self.submit_report) self.Bind(wx.EVT_CLOSE, self.HandleClose) self.Bind(EVT_EXIT_MODEL, self.ExitModel) self.Bind(EVT_DO_UPDATE, self.DoUpdate) self.Bind(EVT_CONCLUDE_UPDATE, self.ConcludeUpdate) self.Bind(EVT_DO_WELCOME, self.ShowWelcome) self.Bind(EVT_PRO_BUTTON, self.EnterProKey) self.tray = tray self.model = model self.lang = None self.exit = False self.submitting = False self.entering_key = False self.pro_dialog = None self.welcome = None model.AddView(self) self.SetSize((600, 600)) menuBar = wx.MenuBar() fileMenu = wx.Menu() fileMenu.Append(self.CLOSE_ID, '', '') fileMenu.Append(self.EXIT_ID, '', '') menuBar.Append(fileMenu, '') editMenu = wx.Menu() editMenu.Append(self.CONFIGURE_ID, '', '') editMenu.AppendSeparator() editMenu.Append(self.RESET_ID, '', '') menuBar.Append(editMenu, '') helpMenu = wx.Menu() helpMenu.Append(self.FAQ_ID, '', '') helpMenu.Append(self.INSTRUCTIONS_ID, '', '') helpMenu.AppendSeparator() helpMenu.Append(self.STATUS_ID, '', '') helpMenu.Append(self.SUBMIT_ID, '', '') helpMenu.AppendSeparator() helpMenu.Append(self.CHECK_ID, '', '') helpMenu.AppendSeparator() if license.get_code(): helpMenu.Append(self.PROFESSIONAL_ID, '', '') else: helpMenu.Append(self.UPGRADE_ID, '', '') helpMenu.Append(self.ABOUT_ID, '', '') menuBar.Append(helpMenu, '') self.SetMenuBar(menuBar) self.CreateStatusBar() self.Bind(wx.EVT_MENU, self.Hide, id = self.CLOSE_ID) self.Bind(wx.EVT_MENU, self.OnExit, id = self.EXIT_ID) self.Bind(wx.EVT_MENU, self.tray.OnConfigure, id = self.CONFIGURE_ID) self.Bind(wx.EVT_MENU, self.tray.OnStatus, id = self.STATUS_ID) self.Bind(wx.EVT_MENU, self.tray.OnAbout, id = self.ABOUT_ID) self.Bind(wx.EVT_MENU, self.OnSubmitReport, id = self.SUBMIT_ID) self.Bind(wx.EVT_MENU, self.OnProButton, id = self.PROFESSIONAL_ID) self.Bind(wx.EVT_MENU, self.OnReset, id = self.RESET_ID) self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, id = self.CHECK_ID) self.Bind(wx.EVT_MENU, self.OnFAQ, id = self.FAQ_ID) self.Bind(wx.EVT_MENU, self.OnInstructions, id = self.INSTRUCTIONS_ID) self.Bind(wx.EVT_MENU, self.OnUpgrade, id = self.UPGRADE_ID) self.UpdateText() def OnFAQ(self, unused): '''Open link to FAQ page.''' webbrowser.open('http://faq.spamexperts.com', True, True) def OnInstructions(self, unused): '''Open link to Basic Instructions page.''' webbrowser.open('http://basicinstructions.spamexperts.com', True, True) def ShowWelcome(self, unused): '''Display welcome message to the user.''' self.welcome = WelcomeDialog(self, self.model.settings) self.welcome.Show() def DoUpdate(self, unused): if hasattr(self.tray.update_thread, 'cancel'): self.tray.update_thread.cancel() self.tray.update_thread = None progress_style = wx.PD_ELAPSED_TIME | wx.PD_ESTIMATED_TIME | wx.PD_REMAINING_TIME | wx.PD_AUTO_HIDE | wx.PD_SMOOTH dlg = wx.ProgressDialog(_('SpamExperts Update'), _('Starting update.'), maximum = 100, style = progress_style) updater = software_update.CompleteUpdate(self.tray) try: updater.balloon = self.tray.UpdateOnClose except AttributeError: print >>sys.stderr, 'Previous update failed. Need to replace SpamExperts.exe ASAP.' updater.update(progress_update_wrapper(dlg)) dlg.Destroy() def ConcludeUpdate(self, unused): progress_style = wx.PD_ELAPSED_TIME | wx.PD_ESTIMATED_TIME | wx.PD_REMAINING_TIME | wx.PD_SMOOTH dlg = wx.ProgressDialog(_('SpamExperts Update'), _('Concluding update.'), maximum = 100, style = progress_style) updater = software_update.CompleteUpdate(self.tray) updater.conclude_update(progress_update_wrapper(dlg)) dlg.Destroy() def OnReset(self, evt): dlg = wx.MessageDialog(self, _('Warning! When you reset the user data all saved information, such as training information and emails from the lists will be lost.\nDo you want to continue?'), _('Reset SpamExperts user data'), wx.ICON_WARNING | wx.YES_NO) if dlg.ShowModal() == wx.ID_YES: self.model.ResetAll() self.model.UpdateViews(refresh = True) dlg.Destroy() def EnterProKey(self, unused): self.entering_key = True while True: result = self.OnProButton() if result is None: self.OnExit(None) break if result is True: break continue self.entering_key = False def OnUpgrade(self, unused = None): self.OnProButton() def OnProButton(self, unused = None): '''Returns True if successful, False if not successful, and None if cancelled.''' d = ProCodeDialog(self, -1, _('Enter professional code')) self.pro_dialog = d result = d.ShowModal() code = d.Code() d.Destroy() self.pro_dialog = None if result == wx.ID_OK: license = license import spamexperts old_code = license.get_code() license.store_code(code) if not license.check_license_validity(): msg = _('Your license is invalid.') d = wx.MessageDialog(None, msg, _('Invalid license'), wx.OK | wx.ICON_ERROR) d.ShowModal() d.Destroy() if old_code is None: license.remove_code() else: license.store_code(old_code) return False data_dir = shell.SHGetFolderPath(0, shellcon.CSIDL_APPDATA, 0, 0) license.update_license() if time.time() > license.get_expiry(): msg = _('Your license has expired and must be renewed.') d = wx.MessageDialog(None, msg, _('Invalid license'), wx.OK | wx.ICON_ERROR) d.ShowModal() d.Destroy() if old_code is None: license.remove_code() else: license.store_code(old_code) return False msg = _('Thank you. Registration complete.') d = wx.MessageDialog(None, msg, _('Valid license'), wx.OK) d.ShowModal() d.Destroy() return True def OnSubmitReport(self, unused): import error_reporter dlg = wx.ProgressDialog(_('SpamExperts'), _('Sending...'), maximum = 100) (succeeded, reason) = error_reporter.ErrorReporter().submit_report(progress_update_wrapper(dlg)) dlg.Destroy() if not succeeded: dlg = wx.MessageDialog(self, _('Could not send error report: ') + reason, _('SpamExperts Error'), wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() def submit_report(self, unused): self.submitting = True dlg = wx.ProgressDialog(_('SpamExperts'), _('Sending...'), maximum = 100) import error_reporter errorReporter = error_reporter.ErrorReporter() (succeeded, reason) = errorReporter.submit_report(progress_update_wrapper(dlg)) dlg.Destroy() if not succeeded: dlg = wx.MessageDialog(self, _('Could not send error report: ') + reason, _('SpamExperts Error'), wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() self.submitting = False def OnExit(self, evt): if self.welcome: if options[('globals', 'verbose')]: print 'Closing welcome' self.welcome.Hide() self.welcome.Close() self.welcome.Destroy() if self.pro_dialog: self.pro_dialog.Hide() self.pro_dialog.Close() self.pro_dialog.Destroy() self.tray.PostError(None) def UpdateView(self, model): if self.lang != model.settings.lang: self.tray.SetupLanguage(model.settings.lang) self.UpdateText() self.lang = model.settings.lang def Hide(self, event = None): wx.Frame.Hide(self) def Show(self, hide = False): if hide: return self.Hide() self.model.UpdateViews(True) self.Raise() wx.Frame.Show(self) def UpdateText(self): self.SetIcon(wx.Icon(get_image_filename('logo-ico'), wx.BITMAP_TYPE_ICO)) logo = wx.Image(get_image_filename('logo', self.tray.lang)).ConvertToBitmap() self.logo.SetBitmap(logo) mb = self.GetMenuBar() item = mb.FindItemById(self.CLOSE_ID) item.SetText(_('&Close')) item.SetHelp(_('Close')) item = mb.FindItemById(self.EXIT_ID) item.SetText(_('E&xit')) item.SetHelp(_('Exit SpamExperts')) item = mb.FindItemById(self.CONFIGURE_ID) item.SetText(_('&Settings')) item.SetHelp(_('Configure SpamExperts')) item = mb.FindItemById(self.RESET_ID) item.SetText(_('Reset SpamExperts User Data')) item.SetHelp(_('Resetting your user data allows you to continue the use of SpamExperts with a fresh training and email database. Note that SpamExperts will require new training on email to be able to properly recognize spam.')) item = mb.FindItemById(self.STATUS_ID) item.SetText(_('&Status')) item.SetHelp(_('Status and Statistics')) item = mb.FindItemById(self.FAQ_ID) item.SetText(_('&Open FAQ Page')) item.SetHelp(_('Open a new browser window to the SpamExperts Frequently Asked Questions (FAQ) page.')) item = mb.FindItemById(self.INSTRUCTIONS_ID) item.SetText(_('Open &Basic Instructions')) item.SetHelp(_('Open a new browser window to a page of basic instructions for SpamExperts.')) item = mb.FindItemById(self.ABOUT_ID) item.SetText(_('&About SpamExperts')) item.SetHelp(_('About SpamExperts and Status')) item = mb.FindItemById(self.SUBMIT_ID) item.SetText(_('S&ubmit Bug Report')) item.SetHelp(_('Submit bug report')) if license.get_code(): item = mb.FindItemById(self.PROFESSIONAL_ID) if not item: item = mb.FindItemById(self.UPGRADE_ID) self.Bind(wx.EVT_MENU, self.OnProButton, id = self.UPGRADE_ID) item.SetText(_('E&nter new license key')) item.SetHelp(_('Enter new license key for SpamExperts Professional')) else: item = mb.FindItemById(self.UPGRADE_ID) if not item: item = mb.FindItemById(self.PROFESSIONAL_ID) self.Bind(wx.EVT_MENU, self.OnUpgrade, id = self.PROFESSIONAL_ID) item.SetText(_('&Upgrade to Professional')) item.SetHelp(_('Enter license key to upgrade to SpamExperts Professional')) item = mb.FindItemById(self.CHECK_ID) item.SetText(_('&Check for SpamExperts Updates')) item.SetHelp(_("SpamExperts checks for updates whenever it is launched, but if you can't wait until then, you can manually check via this menu item.")) mb.SetLabelTop(0, _('&File')) mb.SetLabelTop(1, _('&Edit')) mb.SetLabelTop(2, _('&Help')) self.cls.UpdateText() def OnCheckForUpdate(self, unused): if self.model.isUpdateAvailable(): if self.tray.update_thread == 'in_progress': dlg = wx.MessageDialog(self, _('Update already in progress.'), _('SpamExperts'), wx.OK) dlg.ShowModal() dlg.Destroy() return None try: self.tray.update_thread.cancel() except AttributeError: pass self.tray.update_thread = 'manual' self.tray.CheckForUpdate() else: dlg = wx.MessageDialog(self, _('SpamExperts is already up to date.'), _('SpamExperts'), wx.OK) dlg.ShowModal() dlg.Destroy() def OnCheckMessages(self, event = None): if self.entering_key: return None self.Show() self.Restore() self.Raise() self.model.active = VIEW_UNSURE self.cls.UpdateView(self.model) self.cls.buttonBar.UpdateView(self.model) def HandleClose(self, unused = None): self.Hide() if self.exit: self.tray.PostError('exit') import thread thread.exit() def ExitModel(self, unused = None): self.Hide() if self.welcome: if options[('globals', 'verbose')]: print 'Closing welcome' self.welcome.Hide() self.welcome.Close() self.welcome.Destroy() dlg = wx.ProgressDialog(_('SpamExperts'), _('Closing...'), maximum = 100) self.model.Exit(progress_update_wrapper(dlg)) dlg.Destroy() self.exit = True self.HandleClose() class SettingsDialog(wx.Dialog): def __init__(self, model, tray): if license.get_code(): title = _('SpamExperts Professional Configuration') else: title = _('SpamExperts Home Configuration') pre = wx.PreDialog() pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) pre.Create(None, -1, title = title) self.PostCreate(pre) self.nb = wx.Notebook(self) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.nb, 1, wx.EXPAND | wx.ALL, 3) self.SetSizer(sizer) self.apply = wx.Button(self, label = '') self.setting = Settings(self.nb, model, tray) self.intercepted = InterceptedApps(self.nb, model) self.periodic = PeriodicRetrieval(self.nb, model) self.nb.AddPage(self.setting, '') self.nb.AddPage(self.intercepted, '') self.nb.AddPage(self.periodic, '') self.status_message = wx.StaticText(self) self.status_message.SetForegroundColour(wx.RED) self.apply.Enable(False) self.cancel = wx.Button(self, label = '') self.apply_sizer = wx.BoxSizer(wx.HORIZONTAL) self.apply_sizer.Add(self.status_message, 1, wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, 5) self.apply_sizer.Add(self.apply, 0, wx.ALL, 5) self.apply_sizer.Add(self.cancel, 0, wx.ALL, 5) sizer.Add(self.apply_sizer, 0, wx.EXPAND) self.Bind(wx.EVT_CLOSE, self.Hide) self.Bind(wx.EVT_ICONIZE, self.Hide) self.Bind(wx.EVT_CLOSE, self.Hide) self.cancel.Bind(wx.EVT_BUTTON, self.OnCancel) self.apply.Bind(wx.EVT_BUTTON, self.OnApply) self.tray = tray self.restart_required = False self.model = model self.lang = None self.UpdateText() model.AddView(self) width = []([ size for unused, size in self.intercepted.columns ]) (unused, height) = self.GetBestFittingSize() self.SetSize((width, height)) self.SetIcon(wx.Icon(get_image_filename('logo-ico'), wx.BITMAP_TYPE_ICO)) def OnCancel(self, evt): self.Hide() def OnApply(self, evt): options[('Storage', 'cache_expiry_days')] = int(self.setting.cache_expiry_days.GetValue()) options.update_file(optionsPathname) old_intercepted = self.model.settings.mailapps self.model.SetSettings(self.setting.block.GetValue(), self.setting.modSubj.GetValue(), self.setting.tag.GetValue(), self.setting.lang.GetSelection(), self.intercepted.GetValue(), self.setting.auto_bugreport.GetValue(), self.setting.user_address.GetValue(), self.setting.date_header.GetSelection(), self.setting.auto_update.GetValue(), self.setting.on_startup.GetValue(), self.setting.vertical.GetValue(), self.periodic.GetValue(), self.setting.enable_period.GetValue(), self.setting.period.GetValue(), self.setting.notify_sound.GetValue(), self.setting.enable_balloons.GetValue()) self.apply.Enable(False) if sorted(self.model.settings.mailapps) != sorted(old_intercepted): self.restart_required = True self.UpdateText() def UpdateView(self, model): if self.lang != model.settings.lang: self.tray.SetupLanguage(model.settings.lang) self.UpdateText() self.lang = model.settings.lang def Hide(self, event = None): wx.Dialog.Hide(self) def Show(self, hide = False): if hide: return self.Hide() self.model.UpdateViews(True) self.Raise() wx.Dialog.Show(self) def UpdateText(self): self.nb.SetPageText(0, _('General Settings')) self.nb.SetPageText(1, _('Intercepted Email Applications')) self.nb.SetPageText(2, _('Periodic Retrieval')) self.apply.SetTitle(_('Apply')) self.apply.SetBestFittingSize() self.cancel.SetTitle(_('Close')) self.cancel.SetBestFittingSize() if self.restart_required: label = textwrap.fill(_('In order for these changes to take effect, you must restart your computer.'), 40) self.status_message.SetLabel(label) else: self.status_message.SetLabel('') self.status_message.SetBestFittingSize() self.apply_sizer.Layout() self.GetSizer().Layout() self.setting.UpdateText() self.intercepted.UpdateText() self.periodic.UpdateText() def OnConfigure(self, event = None, page = 0): self.Show() self.Restore() self.nb.SetSelection(page) class StatusDialog(wx.Dialog): def __init__(self, model, tray): title = _('Status and Statistics') pre = wx.PreDialog() pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) pre.Create(None, -1, title = title) self.PostCreate(pre) self.SetBackgroundColour('white') sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) self.status = Status(self, model) sizer.Add(self.status, 0, wx.EXPAND | wx.ALL, 10) self.tray = tray self.model = model self.lang = None self.UpdateText() model.AddView(self) self.Bind(wx.EVT_CLOSE, self.Hide) self.SetIcon(wx.Icon(get_image_filename('logo-ico'), wx.BITMAP_TYPE_ICO)) def UpdateView(self, model): if self.lang != model.settings.lang: self.tray.SetupLanguage(model.settings.lang) self.UpdateText() self.lang = model.settings.lang def Hide(self, event = None): wx.Dialog.Hide(self) def Show(self, hide = False): if hide: return self.Hide() self.model.UpdateViews(True) self.UpdateText() self.Raise() wx.Dialog.Show(self) def UpdateText(self): self.status.UpdateText() widths = [ c.GetSize()[0] for c in self.status.controls ] self.SetSize((w + 60, h)) def OnStatus(self, event = None): self.Show() self.Restore() class AboutDialog(wx.Dialog): def __init__(self, model, tray): if license.get_code(): title = _('About SpamExperts Professional') else: title = _('About SpamExperts Home') pre = wx.PreDialog() pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) pre.Create(None, -1, title = title) self.PostCreate(pre) self.SetBackgroundColour('white') sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) self.about = AboutSE(self, model) sizer.Add(self.about, 0, wx.EXPAND | wx.ALL, 10) self.tray = tray self.model = model self.lang = None self.UpdateText() model.AddView(self) self.Bind(wx.EVT_CLOSE, self.Hide) self.SetIcon(wx.Icon(get_image_filename('logo-ico'), wx.BITMAP_TYPE_ICO)) def UpdateView(self, model): if self.lang != model.settings.lang: self.tray.SetupLanguage(model.settings.lang) self.UpdateText() self.lang = model.settings.lang def Hide(self, event = None): wx.Dialog.Hide(self) def Show(self, hide = False): if hide: return self.Hide() self.model.UpdateViews(True) self.Raise() wx.Dialog.Show(self) def UpdateText(self): self.about.UpdateText() self.SetBestFittingSize() def OnAbout(self, event = None): self.Show() self.Restore() class SEApp(wx.App): def __init__(self, model, tray, *args, **kwargs): self.model = model self.tray = tray wx.App.__init__(self, *args, **kwargs) def OnInit(self): self.window = MainFrame(self.model, self.tray) self.settings_window = SettingsDialog(self.model, self.tray) self.about_window = AboutDialog(self.model, self.tray) self.status_window = StatusDialog(self.model, self.tray) self.SetTopWindow(self.window) provider = wx.SimpleHelpProvider() wx.HelpProvider_Set(provider) return True app = None def main(model, tray): global app try: app = SEApp(model, tray, 0, useBestVisual = True) if options[('globals', 'verbose')]: print 'Entering main GUI loop.' app.MainLoop() if options[('globals', 'verbose')]: print 'Main GUI loop is finished.' except Exception: e = None tray.PostError(str(e)) raise return None if options[('globals', 'verbose')]: print 'Exiting GUI.'